package com.itheima.stock.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.mapper.*;
import com.itheima.stock.pojo.domain.PermissionsTreeDomain;
import com.itheima.stock.pojo.domain.UserAddDomain;
import com.itheima.stock.pojo.domain.UserRoleDomain;
import com.itheima.stock.pojo.entity.*;
import com.itheima.stock.pojo.vo.PermissionAddVo;
import com.itheima.stock.pojo.vo.PermissionTreeNode;
import com.itheima.stock.pojo.vo.PermissionUpdateVo;
import com.itheima.stock.pojo.vo.UserRolesUpdateInfo;
import com.itheima.stock.service.PermissionService;
import com.itheima.stock.service.UserService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.vo.req.AddRoleVo;
import com.itheima.stock.vo.req.LoginReqVo;
import com.itheima.stock.vo.req.UpdateRoleVo;
import com.itheima.stock.vo.resp.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("userService")
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private SysRolePermissionMapper sysRolePermissionMapper;

    @Autowired
    private PermissionService permissionService;

    @Override
    public SysUser findByUserName(String userName) {
        return sysUserMapper.findUserInfoByUserName(userName);
    }



    /**
     * 用户登录功能
     * @param vo
     * @return
     */
    public R<LoginRespExpandVo> login(LoginReqVo vo) {
        //1，判断参数是否合法
        if (vo==null || StringUtils.isBlank(vo.getUsername()) || StringUtils.isBlank(vo.getPassword()) || StringUtils.isBlank(vo.getCode())) {
            return R.error(ResponseCode.DATA_ERROR);
        }
        if(StringUtils.isBlank(vo.getCode())||StringUtils.isBlank(vo.getSessionId())){
            return R.error(ResponseCode.CHECK_CODE_ERROR);
        }
        //判断redis中保存的验证码与输入的验证码是否相同(比较时忽略大小写)
        String redisCode = (String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + vo.getSessionId());
        if(StringUtils.isBlank(redisCode)){
            //验证码过期
            return R.error(ResponseCode.CHECK_CODE_TIMEOUT);
        }
        if (!redisCode.equalsIgnoreCase(vo.getCode())) {
            //验证码错误
            return R.error(ResponseCode.CHECK_CODE_ERROR);
        }

        //2 根据用户名去数据库查询用户信息，获取密码的密文
        SysUser dbUser = sysUserMapper.findUserInfoByUserName(vo.getUsername());
        if(dbUser==null){
            //用户不存在
            return R.error(ResponseCode.ACCOUNT_EXISTS_ERROR);
        }
        //3 调用密码匹配器匹配输入的明文密码和数据库的明文密码
        if (passwordEncoder.matches(vo.getPassword(),dbUser.getPassword())) {
            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
        }
        //4.响应
        LoginRespExpandVo loginRespVo = new LoginRespExpandVo();
        //发现LoginRespVo与SysUser对象属性名称和类型一致
        //必须保证属性名称和类型一致
        BeanUtils.copyProperties(dbUser,loginRespVo);
        //获取指定用户的权限集合 添加获取侧边栏数据和按钮权限的结合信息
        List<SysPermission> permissions = permissionService.getPermissionByUserId(dbUser.getId());
        //获取树状权限菜单数据
        List<PermissionsTreeDomain> permissionTree = permissionService.getPermissionTree(permissions, 0l, true);
        //获取菜单按钮集合
        List<String> authBtnPerms = permissions.stream().filter(per -> !Strings.isNullOrEmpty(per.getCode()) && per.getType() == 3)
                .map(per -> per.getCode()).collect(Collectors.toList());
        loginRespVo.setMenus(permissionTree);
        loginRespVo.setPermissions(authBtnPerms);
        //设置Token
        String info = loginRespVo.getId()+":"+loginRespVo.getRealName();
        String encodeInfo = BaseEncoding.base64().encode(info.getBytes());
        loginRespVo.setAccessToken(encodeInfo);
        return R.ok(loginRespVo);
    }

    /**
     * 根据已知的权限集去递归整理子集
     * @param list
     * @param id
     * @return
     */
    public List<PermissionsTreeDomain> getPermissionFromPid(List<SysPermission> list,Long id){
        List<SysPermission> ans = list.stream().filter(item->Long.compare(id,item.getPid())==0).collect(Collectors.toList());
        List<PermissionsTreeDomain> ans2 = new ArrayList<>();
        for(int i=0;i<ans.size();++i){
            PermissionsTreeDomain permissionsTreeDomain = new PermissionsTreeDomain();
            BeanUtils.copyProperties(ans.get(i),permissionsTreeDomain);
            permissionsTreeDomain.setPath(ans.get(i).getUrl());
            permissionsTreeDomain.setChildren(getPermissionFromPid(list,ans.get(i).getId()));
            ans2.add(permissionsTreeDomain);
        }
        return ans2;
    }
//    @Override 旧版登录
//    public R<LoginRespExpandVo> login(LoginReqVo vo) {
//        //1，判断参数是否合法
//        if (vo==null || StringUtils.isBlank(vo.getUsername()) || StringUtils.isBlank(vo.getPassword()) || StringUtils.isBlank(vo.getCode())) {
//            return R.error(ResponseCode.DATA_ERROR);
//        }
//        if(StringUtils.isBlank(vo.getCode())||StringUtils.isBlank(vo.getSessionId())){
//            return R.error(ResponseCode.CHECK_CODE_ERROR);
//        }
//        //判断redis中保存的验证码与输入的验证码是否相同(比较时忽略大小写)
//        String redisCode = (String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + vo.getSessionId());
//        if(StringUtils.isBlank(redisCode)){
//            //验证码过期
//            return R.error(ResponseCode.CHECK_CODE_TIMEOUT);
//        }
//        if (!redisCode.equalsIgnoreCase(vo.getCode())) {
//            //验证码错误
//            return R.error(ResponseCode.CHECK_CODE_ERROR);
//        }
//
//        //2 根据用户名去数据库查询用户信息，获取密码的密文
//        SysUser dbUser = sysUserMapper.findUserInfoByUserName(vo.getUsername());
//        if(dbUser==null){
//            //用户不存在
//            return R.error(ResponseCode.ACCOUNT_EXISTS_ERROR);
//        }
//        //3 调用密码匹配器匹配输入的明文密码和数据库的明文密码
//        if (passwordEncoder.matches(vo.getPassword(),dbUser.getPassword())) {
//            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
//        }
//        //4.响应
//        LoginRespVo loginRespVo = new LoginRespVo();
////        loginRespVo.setUsername(dbUser.getUsername());
////        loginRespVo.setNickName(dbUser.getNickName());
////        loginRespVo.setPhone(dbUser.getPhone());
////        loginRespVo.setId(dbUser.getId());
//
//        //发现LoginRespVo与SysUser对象属性名称和类型一致
//        //必须保证属性名称和类型一致
//        BeanUtils.copyProperties(dbUser,loginRespVo);
//        return R.ok(loginRespVo);
//    }

    /**
     * 生成图片验证码功能
     * @return
     */
    @Override
    public R<Map> getCaptchaCode() {
        //1.生成图片验证码
        /**
         * 参数1：图片的宽度
         * 参数2：图片高度
         * 参数3：图片中包含验证码的长度
         * 参数4：干扰线数量
         */
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(250, 40, 4, 5);
        //设置背景颜色
        captcha.setBackground(Color.LIGHT_GRAY);
        //自定义生成校验码的规则
//        captcha.setGenerator(new CodeGenerator() {
//            @Override
//            public String generate() {
//                //自定义校验码生成的逻辑
//                return null;
//            }
//
//            @Override
//            public boolean verify(String s, String s1) {
//                //匹配校验码逻辑
//                return false;
//            }
//        });

        //获取验证码
        String checkcode = captcha.getCode();
        //获取经过base64编码处理的图片数据
        String imageData = captcha.getImageBase64();
        //生成sessionId 转换成String避免精度丢失
        String sesstionId = String.valueOf(idWorker.nextId());
        log.info("当前生成的图片校验码：{}，会话id：{}",checkcode,sesstionId);
        //将sessionId作为key，校验码作为value保存在redis中(使用redis模拟session的行为，通过过期时间设置)
        redisTemplate.opsForValue().set(StockConstant.CHECK_PREFIX+sesstionId,checkcode,5, TimeUnit.MINUTES);
        //组装数据
        Map<String,String> data = new HashMap<>();
        data.put("imageData",imageData);
        data.put("sessionId",sesstionId);
        //响应数据
        return R.ok(data);
    }

    /**
     * 多条件综合查询用户分页信息
     * @param page
     * @param pageSize
     * @param userName
     * @param nickName
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public R<PageResult<SysUser>> getUserInfoByPage(Integer page, Integer pageSize, String userName, String nickName, Date startTime, Date endTime) {
        //1.设置PageHelper分页参数
        PageHelper.startPage(page,pageSize);
        //2.调用mapper查询
        List<SysUser> users = sysUserMapper.getUsersByParam(userName, nickName, startTime, endTime);
        //3.组装PageResult对象
        PageInfo<SysUser> pageInfo = new PageInfo<>(users);
        PageResult<SysUser> pageResult = new PageResult(pageInfo);
        return R.ok(pageResult);
    }

    @Override
    public R<String> addUser(UserAddDomain userAddDomain) {
        SysUser sysUser = sysUserMapper.selectByUsername(userAddDomain.getUsername());
        if(null!=sysUser){
            return R.error("操作失败，用户名已存在");
        }
        SysUser sysUserAdd = new SysUser();
        sysUserAdd.setUsername(userAddDomain.getUsername());
        sysUserAdd.setId(idWorker.nextId());
        sysUserAdd.setNickName(userAddDomain.getNickName());
        sysUserAdd.setRealName(userAddDomain.getRealName());
        sysUserAdd.setPassword(passwordEncoder.encode(userAddDomain.getPassword()));
        sysUserAdd.setPhone(userAddDomain.getPhone());
        sysUserAdd.setEmail(userAddDomain.getEmail());
        sysUserAdd.setStatus(userAddDomain.getStatus());
        sysUserAdd.setSex(userAddDomain.getSex());
        sysUserAdd.setCreateWhere(userAddDomain.getCreateWhere());
        sysUserAdd.setCreateTime(new Date());
        int flag = sysUserMapper.insertSelective(sysUserAdd);
        if(flag!=1){
            return R.error("操作失败");
        }
        return R.ok("操作成功");
    }


    /**
     * 获取用户具有的角色信息，以及所有角色信息
     * @param id
     * @return
     */
    @Override
    public R<UserRoleDomain> getUserRoleInfo(String id) {
        //1.获取该用户对应的角色id
        List<Long> userRoleIds = sysUserMapper.getUserRoleIds(id);
        //2.获取所有的角色信息
        List<SysRole> RoleInfos = sysRoleMapper.selectAll();
        UserRoleDomain userRoleDomain = new UserRoleDomain();
        userRoleDomain.setAllRole(RoleInfos);
        userRoleDomain.setOwnRoleIds(userRoleIds);
        return R.ok(userRoleDomain);
    }

    @Override
    public R<String> updateUserRole(UserRolesUpdateInfo userRolesUpdateInfo) {
        //1.删除用户原来的角色
        int result = sysUserRoleMapper.deleteByUserId(userRolesUpdateInfo.getUserId());
        //2.添加新的用户角色信息
        //获取需要更新的角色信息
        List<Long> roleIds = userRolesUpdateInfo.getRoleIds();
        //生成用户角色信息数据，进行批量插入
        List<SysUserRole> arr = new ArrayList<>();
        Date now = new Date();
        for(int i=0;i<roleIds.size();++i){
            arr.add(new SysUserRole(idWorker.nextId(),userRolesUpdateInfo.getUserId(),roleIds.get(i),now));
        }
        result = sysUserRoleMapper.insertBatch(arr);
        return R.ok("操作成功");
    }

    /**
     * 批量删除用户信息
     * @param userIds
     * @return
     */
    @Override
    public R<String> deleteUserByIds(List<Long> userIds) {
        //1.删除用户角色信息
        int result = sysUserRoleMapper.deleteByIds(userIds);
        log.info("删除了"+result+"条用户角色表信息");
        //2.删除用户信息
        int cnt2 = sysUserMapper.deleteBatchByIds(userIds);
        return R.ok("操作成功");
    }

    /**
     * 根据用户id查询用户信息
     */
    @Override
    public R<SysUser> getUserInfo(Long userId) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        return R.ok(sysUser);
    }

    /**
     * 根据id更新用户基本信息
     */
    @Override
    public R<String> updateUserById(SysUser sysUser) {
        int t = sysUserMapper.updateByPrimaryKey(sysUser);
        if(t==0){
            return R.error("更新失败");
        }
        return R.error("操作成功");
    }

    /**
     * 分页查询当前角色信息
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult<SysRole>> queryRolesByPage(Integer page, Integer pageSize) {
        PageHelper.startPage(page,pageSize);
        List<SysRole> sysRoles = sysRoleMapper.selectAll();
        PageInfo<SysRole> pageInfo = new PageInfo<>(sysRoles);
        PageResult<SysRole> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    /**
     * 树状结构回显权限集合,底层通过递归获取权限数据集合
     * @return
     */
    @Override
    public R<List<PermissionsTreeDomain>> getAllPermissions() {
        List<PermissionsTreeDomain> result = getPermissions(0l);
        return R.ok(result);
    }

    /**
     * 添加角色和角色关联权限
     * @param addRoleVo
     * @return
     */
    @Override
    public R<String> addRoleConnection(AddRoleVo addRoleVo) {
        //1.插入角色信息
        Long id = idWorker.nextId();
        SysRole sysRole = new SysRole(id,addRoleVo.getName(),addRoleVo.getDescription(),1,new Date(),new Date(),0);
        sysRoleMapper.insert(sysRole);
        //2.插入角色权限信息
        //封装角色权限信息
        List<SysRolePermission> arr = new ArrayList<>();
        List<Long> permissionsIds = addRoleVo.getPermissionsIds();
        for(int i=0;i<permissionsIds.size();++i){
            arr.add(new SysRolePermission(idWorker.nextId(),id,permissionsIds.get(i),new Date()));
        }
        int t = sysRolePermissionMapper.insertBatch(arr);
        return R.ok("操作成功");
    }

    /**
     * 根据角色id查找对应的权限id集合
     * @param id
     * @return
     */
    @Override
    public R<List<Long>> getRolePermission(Long id) {
        List<Long> rolePermissions = sysRolePermissionMapper.getRolePermission(id);
        return R.ok(rolePermissions);
    }


    /**
     * 更新角色信息，包含角色关联的权限信息
     * @param updateRoleVo
     * @return
     */
    @Override
    public R<String> updateRolePermission(UpdateRoleVo updateRoleVo) {
        //1.删除该角色原本的权限
        int t =sysRolePermissionMapper.deleteByRoleId(updateRoleVo.getId());
        log.info("删除了{}条角色权限信息，角色id为{}",t,updateRoleVo.getId());
        //2.更新角色信息
        SysRole sysRole = new SysRole();
        sysRole.setId(updateRoleVo.getId());
        sysRole.setName(updateRoleVo.getName());
        sysRole.setDescription(updateRoleVo.getDescription());
        sysRoleMapper.updateByPrimaryKeySelective(sysRole);
        //3.与角色关联新的权限
        List<Long> permissionIds = updateRoleVo.getPermissionsIds();
        List<SysRolePermission> arr = new ArrayList<>();
        for(int i=0;i<permissionIds.size();++i){
            arr.add(new SysRolePermission(idWorker.nextId(),updateRoleVo.getId(),permissionIds.get(i),new Date()));
        }
        int tt = sysRolePermissionMapper.insertBatch(arr);
        log.info("插入了{}条角色权限信息",tt);
        return R.ok("操作成功");
    }

    /**
     * 根据角色id删除角色信息
     * @param id
     * @return
     */
    @Override
    public R<String> deleteRoleById(Long id) {
        //1.删除与该角色关联的权限信息
        sysRolePermissionMapper.deleteByRoleId(id);
        //2.删除与该角色关联的用户角色信息
        sysUserRoleMapper.deleteByRoleId(id);
        //3.删除该角色信息
        sysRoleMapper.deleteByPrimaryKey(id);
        return R.ok("操作成功");
    }

    /**
     * 更新用户状态
     * @param id
     * @param status
     * @return
     */
    @Override
    public R<String> updateUserStatus(Long id, Integer status) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setStatus(status);
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
        return R.ok("操作成功");
    }

    /**
     * 查询所有权限集合
     * @return
     */
    @Override
    public R<List<SysPermission>> getAllPermissionsNoOrder() {
        List<SysPermission> sysPermissions = sysPermissionMapper.selectAll();
        return R.ok(sysPermissions);
    }

    /**
     * 查询所有权限的简略信息，以便以树状结构进行回显
     * @return
     */
    @Override
    public R<List<PermissionTreeNode>> showPermissionTree() {
        List<PermissionTreeNode> permissionTreeNodes = sysPermissionMapper.selectAllSimpleInfo();
        return R.ok(permissionTreeNodes);
    }

    /**
     * 添加权限
     * @param vo
     * @return
     */
    @Override
    public R<String> addPermission(PermissionAddVo vo) {
        SysPermission sysPermission = new SysPermission(idWorker.nextId(),vo.getCode(),vo.getTitle(),vo.getIcon(),vo.getPerms(),vo.getUrl(),vo.getMethod(),vo.getName(),vo.getPid(),vo.getOrderNum(),vo.getType(),1,new Date(),null,1);
        int result = sysPermissionMapper.insert(sysPermission);
        if(result==0){
            return R.error("插入失败");
        }
        return R.ok("添加成功");
    }

    /**
     * 更新权限
     * @param vo
     * @return
     */
    @Override
    public R<String> updatePermission(PermissionUpdateVo vo) {
        SysPermission sysPermission = new SysPermission(vo.getId(),vo.getCode(),vo.getTitle(),vo.getIcon(),vo.getPerms(),vo.getUrl(),vo.getMethod(),vo.getName(),vo.getPid(),vo.getOrderNum(),vo.getType(),1,null,new Date(),1);
        int result = sysPermissionMapper.updateByPrimaryKeySelective(sysPermission);
        if(result==0){
            return R.error("插入失败");
        }
        return R.ok("更新成功");
    }

    /**
     * 根据id删除权限
     * @param id
     * @return
     */
    @Override
    public R<String> deletePermissionById(Long id) {
        int i = sysRolePermissionMapper.deleteByPrimaryKey(id);
        return R.ok("删除成功");
    }


    public List<PermissionsTreeDomain> getPermissions(Long id){
        List<PermissionsTreeDomain> result = sysPermissionMapper.selectAllByPid(id);
        for(int i=0;i<result.size();++i){
            result.get(i).setChildren(getPermissions(result.get(i).getId()));
        }
        return result;
    }
}
